package com.mathdroid.modules;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.LinkedList;

import javax.swing.SwingWorker;

import com.mathdroid.model.simulation.CombatLog;
import com.mathdroid.model.simulation.SimulationStatistics;

/**
 * This SwingWorker distributes the workload to more SwingWorkers and shows
 * a progress frame.
 */
public final class Simulator extends SwingWorker<Void, Void> 
implements PropertyChangeListener {

	/**
	 * The total iterations for the simulation.
	 */
	private int iterations = 0;
	
	/**
	 * The amount of physical system cores.
	 */
	private final int cores = Runtime.getRuntime().availableProcessors();
	
	/**
	 * Every time a SwingWorker children increased their progress by one 
	 * percent, this counter is increased by one.
	 */
	private int loopCounter = 0;
	
	/**
	 * The current tasks.
	 */
	private Module[] tasks = new Module[this.cores];
	
	/**
	 * The combat logs.
	 */
	private LinkedList<CombatLog> logs = new LinkedList<CombatLog>();
	
	/**
	 * The current active module for the current specialization.
	 */
	private Module activeModule = null;
	
	/**
	 * The simulation statistics summary.
	 */
	private SimulationStatistics statistics = new SimulationStatistics();

	/**
	 * Create a new Simulator. This SwingWorker should be executed only once.
	 * @param i Iterations.
	 * @param m ClassModule.
	 */
	public Simulator(final int i, final Module m) {
		this.iterations = i;
		this.activeModule = m;
	}

	@Override
	public void propertyChange(final PropertyChangeEvent e) {
		if ("progress".equals(e.getPropertyName())) {
			this.loopCounter++;
			if (this.loopCounter % this.cores == 0) {
					this.setProgress(this.loopCounter / this.cores);
			}
		}
	}

	@Override
	protected Void doInBackground() throws Exception {		
		
		for (int i = 0; i < this.cores; i++) {
			Module m = this.activeModule.getInstance();
			this.tasks[i] = m;
			m.addPropertyChangeListener(this);
			m.setIterations(this.iterations  / this.cores);
			m.execute();
		}
		
		final LinkedList<SimulationStatistics> tempStatistics = new
				LinkedList<SimulationStatistics>();
		
		for (int i = 0; i < this.cores; i++) {
			this.tasks[i].get();
			this.logs.add(this.tasks[i].getCombatLog());
			tempStatistics.add(this.tasks[i].getStatistics());
		}
		
		this.statistics.merge(tempStatistics);
		
		return null;
	}
	
	@Override
	public void done() {
		this.loopCounter = 0;
		
		if (this.isCancelled()) {
			for (int i = 0; i < this.cores; i++) {
				this.tasks[i].cancel(true);
			}
		}
	}
	
	/**
	 * Get the combat logs.
	 * @return Combat logs.
	 */
	public LinkedList<CombatLog> getCombatLogs() {
		return this.logs;
	}
	
	/**
	 * Get the summary of simulation statistics.
	 * @return Get the summary of statistics.
	 */
	public SimulationStatistics getStatistics() {
		return this.statistics;
	}

}
